﻿using System;
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using System.Web;
using log4net;

namespace FT.Architecture.Utils.Log
{
    public class Log4NetLogger : ILogger
    {
        private readonly string name;
        private readonly ILog log;
        private delegate void DebugDelegate(object message, Exception ex);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        public Log4NetLogger(string name)
        {
            this.name = name;
            log = LogManager.GetLogger(this.name);
        }

        /// <summary>
        /// Logger's name
        /// </summary>
        public string Name
        {
            get { return name; }
        }

        /// <summary>
        /// Debug
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public void Debug(string message, params object[] args)
        {
            Debug(string.Format(message, args), (Exception) null);
        }

         /// <summary>
        /// Debug
        /// </summary>
        /// <param name="message"></param>
        /// <param name="ex"></param>
        public void Debug(string message, Exception ex)
        {
            Log(log.Debug, message, ex, log.IsDebugEnabled);
        }

        /// <summary>
        /// Error
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public void Error(string message, params object[] args)
        {
            Error(string.Format(message, args), (Exception)null);
        }

        /// <summary>
        /// Error
        /// </summary>
        /// <param name="message"></param>
        /// <param name="ex"></param>
        public void Error(string message, Exception ex)
        {
            Log(log.Error, message, ex, log.IsErrorEnabled);
        }

        /// <summary>
        /// Warning
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public void Warn(string message, params object[] args)
        {
            Warn(string.Format(message, args), (Exception)null);
        }

        /// <summary>
        /// Warning
        /// </summary>
        /// <param name="message"></param>
        /// <param name="ex"></param>
        public void Warn(string message, Exception ex)
        {
            Log(log.Warn, message, ex, log.IsWarnEnabled);
        }

        /// <summary>
        /// Info
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public void Info(string message, params object[] args)
        {
            Info(string.Format(message, args), (Exception)null);
        }

        /// <summary>
        /// Info
        /// </summary>
        /// <param name="message"></param>
        /// <param name="ex"></param>
        public void Info(string message, Exception ex)
        {
            Log(log.Info, message, ex, log.IsInfoEnabled);
        }

        private void Log(DebugDelegate logMethod, string message, Exception ex, bool enabled)
        {
            if (!enabled)
                return;

            string callingExpression;
            
            MethodBase method = GetCallingMethod();
            Type type = method != null ? method.ReflectedType : null;
            Thread thread = Thread.CurrentThread;

            HttpContext context = HttpContext.Current;

            

            if (type != null)
                callingExpression = type.Name + "." + method.Name;
            else callingExpression = "?";

            string currentPage = "";
            if (context != null)
            {
                currentPage = " " + context.Request.Path;
            }

            logMethod(string.Format("[T:{2} {0}{3}] {1}", callingExpression, message, thread.ManagedThreadId, currentPage), ex);
        }

        private static MethodBase GetCallingMethod()
        {
            MethodBase method;
            var stackTrace = new StackTrace();
            int i = 0;
            do
            {
                method = stackTrace.GetFrame(i).GetMethod();
                i++;
            } 
            while (method != null && method.ReflectedType == typeof(Log4NetLogger));

            return method;
        }
    }
}
